ThreadLocal源码分析

ThreadLocal即线程本地变量,它的设计并不是用来处理并发的。它被用来为每个线程维护独立的变量副本。这种变量在线程的生命周期内起作用,减少同一个线程内多个函数或者组件之间一些公共变量的传递的复杂度。

明白了ThreadLocal设计的初衷,我们接着看看它的实现,首先我们看它的构造方法。

1
2
public ThreadLocal() {
}

内部是一个空的实现

设置元素

往ThreadLocal中设置一个对象是通过set方法来实现的

1
2
3
4
5
6
7
8
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
}

首先在set方法中先获取当前的线程对象,然后通过getMap获取一个ThreadLocalMap对象。

1
2
3
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}

getmap方法返回的Thread 内部的成员threadLocals,它就是一个ThreadLocalMap对象是由Thread对象维护的。在线程中首次使用ThreadLocal的set时getMap返回的结果为null,它需要调用createMap来创建。否则就通过map的set方法来将value保存,这里的key就是我们的ThreadLocal对象。

1
2
3
void createMap(Thread t, T firstValue) {
t.threadLocals = new ThreadLocalMap(this, firstValue);
}

首次创建是带有一个初始值的。我们看看ThreadLocalMap的构造方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
static class ThreadLocalMap {
static class Entry extends WeakReference<ThreadLocal<?>> {
/** The value associated with this ThreadLocal. */
Object value;
Entry(ThreadLocal<?> k, Object v) {
super(k);
value = v;
}
}
private static final int INITIAL_CAPACITY = 16;
private Entry[] table;
private int size = 0;
private int threshold;

ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) {
table = new Entry[INITIAL_CAPACITY];
int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);
table[i] = new Entry(firstKey, firstValue);
size = 1;
setThreshold(INITIAL_CAPACITY);
}
……
}

ThreadLocalMap初始的大小为16个Entry的数组,注意这里的Entry是一个WeakRefrence<LocalThread<?>>,它通过ThreadLocal的hashcode映射到对应的索引处。随后通过第一个值firstValue创建第一个Entry放到相应的位置。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
private void set(ThreadLocal<?> key, Object value) {
Entry[] tab = table;
int len = tab.length;
int i = key.threadLocalHashCode & (len-1);

for (Entry e = tab[i];
e != null;
e = tab[i = nextIndex(i, len)]) {
ThreadLocal<?> k = e.get();

if (k == key) {
e.value = value;
return;
}

if (k == null) {
replaceStaleEntry(key, value, i);
return;
}
}

tab[i] = new Entry(key, value);
int sz = ++size;
if (!cleanSomeSlots(i, sz) && sz >= threshold)
rehash();
}

ThreadLocalMap的set方法首先通过hashcode计算要保存value的索引,随后从该索引处开始便利table,如果key值同e.get(),则说明找到了对应的Entry,则直接更新对应的value即可,否则如果k == null 说明ThreadLocal有可能已经被Gc回收,还记得Entry的key是弱引用,这时候需要将value的值重新替换这个失效的Entry即可。否则即使没找到对应的key,则为其生成一个新的Entry,并增加计数,如果size大于threadhold了需要对map的大小进行调整。

获取元素

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public T get() {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null)
return (T)e.value;
}
return setInitialValue();
}

private T setInitialValue() {
T value = initialValue();
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
return value;
}

获取元素是通过get元素完成的,首先获取到当前线程,通过该线程取到线程的ThreadLocalMap对象map,通过map以ThreadLocal当前对象作为key获取到Entry,如果Entry不为null,则返回相关的值。否则会返回一个初始值。这个初始值可以由我们自己通过initalValue来指定,默认返回null.

移除元素

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public void remove() {
ThreadLocalMap m = getMap(Thread.currentThread());
if (m != null)
m.remove(this);
}

private void remove(ThreadLocal key) {
Entry[] tab = table;
int len = tab.length;
int i = key.threadLocalHashCode & (len-1);
for (Entry e = tab[i];
e != null;
e = tab[i = nextIndex(i, len)]) {
if (e.get() == key) {
e.clear();
expungeStaleEntry(i);
return;
}
}
}

移除元素是通过ThreadLocalMap的remove来删除的,这个操作首先会去通过当前threadlocal对象计算在map中的索引,然后通过删除和当前key相同的那一项。

坚持原创技术分享,您的支持将鼓励我继续创作!